import time
from typing import Sequence, Union, Callable, Any
from typing_extensions import Self, NoReturn
import allure
from selenium.webdriver import ActionChains
from seleniumwire.request import Request

from common.core import wait
from common.core.api.base import WebdriverApi
from common.core.api.element_api import ElementApi
from common.core.globals import PageRouterManager, p, PageObject
from common.core.meta import Xpath
from common.pkg.log import INFO

from common.recording import selfmethod_desc


# 执行脚本
class ExecuteScript(WebdriverApi):
    def action(self, script: str, *args) -> Any:
        return p.webdriver.execute_script(script, *args)

    @selfmethod_desc("执行脚本 | <script>")
    def __call__(self, script: str, *args, **kwargs) -> Any:
        self.set_conf(**kwargs)
        return self.run(script, *args)


# 添加截图
class AddScreen(WebdriverApi):
    @selfmethod_desc("添加截图 | <msg>")
    def __call__(self, msg: str = "", **kwargs) -> None:
        picture = p.webdriver.get_screenshot_as_png()
        name = time.strftime(f"%Y-%m-%d %H:%M:%S: {msg}", time.localtime())
        allure.attach(picture, name, allure.attachment_type.PNG)
        time.sleep(0.5)


# 页面跳转
class Get(WebdriverApi):
    def action(self, url: str) -> None:
        p.webdriver.get(url)
        wait.UrlContains(url).wait()
        wait.DomLoadComplete().wait()

    @selfmethod_desc("网址跳转 | <url>")
    def __call__(self, url, **kwargs) -> None:
        self.set_conf(**kwargs)

        return self.run(url)


# 窗口跳转
class SwitchHandler(WebdriverApi):
    def action(self, i: int) -> None:
        handlers = p.webdriver.window_handles
        p.webdriver.switch_to.window(handlers[i])
        wait.DomLoadComplete().wait()

    @selfmethod_desc("窗口跳转 | 第<i+1>")
    def __call__(self, i: int, **kwargs) -> None:
        self.set_conf(**kwargs)

        return self.run(i)


# 页面跳转
class Redirect(WebdriverApi):
    def action(self, page_name: str) -> PageObject:
        return PageRouterManager.get(page_name)

    @selfmethod_desc("页面跳转 | <page_name>")
    def __call__(self, page_name: str, **kwargs) -> PageObject:
        self.set_conf(**kwargs)

        return self.run(page_name)


# 框架切换
class SwitchToFrame(WebdriverApi):
    def action(self, locator: Union[Xpath, str]) -> None:
        p.webdriver.switch_to.frame(p.find(locator).get_element())

    @selfmethod_desc("框架跳转 | <locator>")
    def __call__(self, locator: Union[Xpath, str], **kwargs) -> None:
        self.set_conf(**kwargs)

        return self.run(locator)


# 跳出框架
class SwitchToParent(WebdriverApi):
    def action(self) -> None:
        p.webdriver.switch_to.parent_frame()

    @selfmethod_desc("框架跳转 | 父框架")
    def __call__(self, **kwargs) -> None:
        self.set_conf(**kwargs)
        return self.run()


# 页面滚动
class ScrollTo(WebdriverApi):
    def action(self, x: int, y: int) -> None:
        p.webdriver.execute_script(f"window.scrollTo({x}, {y});")

    @selfmethod_desc("窗口滚动 | [<x> <y>]")
    def __call__(self, x: int, y: int, **kwargs) -> None:
        self.set_conf(**kwargs)

        return self.run(x, y)


# 退出浏览器
class DriverQuit(WebdriverApi):
    def action(self) -> None:
        p.webdriver.quit()
        # p.clear_page()

    @selfmethod_desc("浏览器退出")
    def __call__(self, **kwargs) -> None:
        self.set_conf(**kwargs)

        return self.run()


# 动作链
class UserActionChains(WebdriverApi):
    def __init__(self, alias: str = None):
        super().__init__(alias=alias)
        self.chains: list[tuple[UserActionChains.ActionChainsApi, tuple, dict]] = []

    def __call__(self) -> Self:
        return self

    class ActionChainsApi(ElementApi):
        def __get__(self, action_chains: "UserActionChains", owner: type) -> Self:
            self.action_chains = action_chains
            return self

        def append_action(self, action: Self, *args, **kwargs):
            self.action_chains.chains.append((action, args, kwargs))

    class MoveToElement(ActionChainsApi):
        @selfmethod_desc("动作链悬浮 | <element_name>")
        def action(self, element_name) -> NoReturn:
            locator = p.get_locator(element_name)
            element = self.get_element(locator)
            ActionChains(p.webdriver).move_to_element(element).perform()

        @selfmethod_desc("添加动作链悬浮 | <element_name>", allure_step=False)
        def __call__(self, element_name: str, **kwargs) -> "UserActionChains":
            self.set_conf(**kwargs)

            self.append_action(self, element_name)
            return self.action_chains

    class Click(ActionChainsApi):
        @selfmethod_desc("动作链点击 | <element_name>")
        def action(self, element_name: Union[Xpath, str]) -> NoReturn:
            locator = p.get_locator(element_name)
            element = self.get_element(locator)
            try:
                ActionChains(p.webdriver).click(element).perform()
            except Exception as e:
                INFO.warning(f"因 {str(e)} 异常使用 javascript 执行点击操作")
                p.execute_script("arguments[0].click()", element)

        @selfmethod_desc("添加动作链点击 | <element_name>", allure_step=False)
        def __call__(self, element_name: Union[Xpath, str], **kwargs) -> "UserActionChains":
            self.set_conf(**kwargs)

            self.append_action(self, element_name)
            return self.action_chains

    class Perform(ActionChainsApi):
        def action(self) -> NoReturn:
            for obj, args, kwargs in self.action_chains.chains:
                obj.run(*args, **kwargs)

        @selfmethod_desc("执行动作链")
        def __call__(self, **kwargs) -> NoReturn:
            self.set_conf(**kwargs)
            self.run()

    move_to_element = MoveToElement("动作链悬停")
    click = Click("动作链点击")
    perform = Perform("动作链执行")


# 捕获请求
class CatchRequest(WebdriverApi):
    def action(
            self,
            path: str,
            action_func: Union[Callable[[PageObject], Any], Sequence[Callable[[PageObject], Any]]],
            time_out=5, step=0.5
    ) -> Request:
        del p.webdriver.requests
        if isinstance(action_func, Sequence):
            [func(p) for func in action_func]
        else:
            action_func(p)
        x, y = divmod(float(time_out), float(step))
        for _ in range(int(x) + 1 if y else int(x)):
            for request in p.webdriver.requests:  # type: Request
                if request.response and str(request.url).endswith(path):
                    return request
            time.sleep(step)
        else:
            raise TimeoutError("当前未捕获到path为 %s 的请求获取请求超时" % path)

    @selfmethod_desc("接口捕获 | <path>")
    def __call__(
            self,
            path: str,
            action_func: Union[Callable[[PageObject], Any], Sequence[Callable[[PageObject], Any]]],
            time_out=5, step=0.5, **kwargs
    ) -> Request:
        self.set_conf(**kwargs)

        return self.run(path, action_func, time_out, step)
